/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
define(["require", "exports", "./protocol.const", "./typescriptServiceClient", "./languageProvider", "./utils/typingsStatus", "./utils/versionStatus", "./utils/convert"], function (require, exports, PConst, typescriptServiceClient_1, languageProvider_1, typingsStatus_1, versionStatus_1, convert_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    //import LogDirectoryProvider from './utils/logDirectoryProvider';
    // Style check diagnostics that can be reported as warnings
    const styleCheckDiagnostics = [
        6133,
        6138,
        7027,
        7028,
        7029,
        7030 // not all code paths return a value
    ];
    class TypeScriptServiceClientHost {
        constructor(descriptions, plugins, commandManager, vscode) {
            this.commandManager = commandManager;
            this.vscode = vscode;
            this.languages = [];
            this.languagePerId = new Map();
            this.disposables = [];
            this.reportStyleCheckAsWarnings = true;
            const handleProjectCreateOrDelete = () => {
                this.client.execute('reloadProjects', null, false);
                this.triggerAllDiagnostics();
            };
            const handleProjectChange = () => {
                setTimeout(() => {
                    this.triggerAllDiagnostics();
                }, 1500);
            };
            const configFileWatcher = vscode.workspace.createFileSystemWatcher('**/[tj]sconfig.json');
            this.disposables.push(configFileWatcher);
            configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this.disposables);
            configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this.disposables);
            configFileWatcher.onDidChange(handleProjectChange, this, this.disposables);
            this.client = new typescriptServiceClient_1.default(this, plugins, this.vscode);
            this.disposables.push(this.client);
            this.versionStatus = new versionStatus_1.default(resource => this.client.normalizePath(resource), vscode);
            this.disposables.push(this.versionStatus);
            this.typingsStatus = new typingsStatus_1.default(this.client);
            this.ataProgressReporter = new typingsStatus_1.AtaProgressReporter(this.client, vscode);
            for (const description of descriptions) {
                const manager = new languageProvider_1.default(this.client, description, this.commandManager, this.typingsStatus, this.vscode);
                this.languages.push(manager);
                this.disposables.push(manager);
                this.languagePerId.set(description.id, manager);
            }
            this.client.ensureServiceStarted();
            this.client.onReady(() => {
                if (!this.client.apiVersion.has230Features()) {
                    return;
                }
                const languages = new Set();
                for (const plugin of plugins) {
                    for (const language of plugin.languages) {
                        languages.add(language);
                    }
                }
                if (languages.size) {
                    const description = {
                        id: 'typescript-plugins',
                        modeIds: Array.from(languages.values()),
                        diagnosticSource: 'ts-plugins',
                        isExternal: true
                    };
                    const manager = new languageProvider_1.default(this.client, description, this.commandManager, this.typingsStatus, this.vscode);
                    this.languages.push(manager);
                    this.disposables.push(manager);
                    this.languagePerId.set(description.id, manager);
                }
            });
            this.client.onTsServerStarted(() => {
                this.triggerAllDiagnostics();
            });
            this.vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables);
            this.configurationChanged();
        }
        dispose() {
            while (this.disposables.length) {
                const obj = this.disposables.pop();
                if (obj) {
                    obj.dispose();
                }
            }
            this.typingsStatus.dispose();
            this.ataProgressReporter.dispose();
        }
        get serviceClient() {
            return this.client;
        }
        reloadProjects() {
            this.client.execute('reloadProjects', null, false);
            this.triggerAllDiagnostics();
        }
        handles(file) {
            return !!this.findLanguage(file);
        }
        configurationChanged() {
            const config = this.vscode.workspace.getConfiguration('typescript');
            this.reportStyleCheckAsWarnings = config.get('reportStyleChecksAsWarnings', true);
        }
        findLanguage(file) {
            return __awaiter(this, void 0, void 0, function* () {
                try {
                    const doc = yield this.vscode.workspace.openTextDocument(this.client.asUrl(file));
                    return this.languages.find(language => language.handles(file, doc));
                }
                catch (_a) {
                    return undefined;
                }
            });
        }
        triggerAllDiagnostics() {
            for (const language of this.languagePerId.values()) {
                language.triggerAllDiagnostics();
            }
        }
        /* internal */ populateService() {
            // See https://github.com/Microsoft/TypeScript/issues/5530
            this.vscode.workspace.saveAll(false).then(() => {
                for (const language of this.languagePerId.values()) {
                    language.reInitialize();
                }
            });
        }
        /* internal */ syntaxDiagnosticsReceived(event) {
            const body = event.body;
            if (body && body.diagnostics) {
                this.findLanguage(body.file).then(language => {
                    if (language) {
                        language.syntaxDiagnosticsReceived(this.client.asUrl(body.file), this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
                    }
                });
            }
        }
        /* internal */ semanticDiagnosticsReceived(event) {
            const body = event.body;
            if (body && body.diagnostics) {
                this.findLanguage(body.file).then(language => {
                    if (language) {
                        language.semanticDiagnosticsReceived(this.client.asUrl(body.file), this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
                    }
                });
            }
        }
        /* internal */ configFileDiagnosticsReceived(event) {
            // See https://github.com/Microsoft/TypeScript/issues/10384
            const body = event.body;
            if (!body || !body.diagnostics || !body.configFile) {
                return;
            }
            (this.findLanguage(body.configFile)).then(language => {
                if (!language) {
                    return;
                }
                if (body.diagnostics.length === 0) {
                    language.configFileDiagnosticsReceived(this.client.asUrl(body.configFile), []);
                }
                else if (body.diagnostics.length >= 1) {
                    this.vscode.workspace.openTextDocument(this.vscode.Uri.file(body.configFile)).then((document) => {
                        let curly = undefined;
                        let nonCurly = undefined;
                        let diagnostic;
                        for (let index = 0; index < document.lineCount; index++) {
                            const line = document.lineAt(index);
                            const text = line.text;
                            const firstNonWhitespaceCharacterIndex = line.firstNonWhitespaceCharacterIndex;
                            if (firstNonWhitespaceCharacterIndex < text.length) {
                                if (text.charAt(firstNonWhitespaceCharacterIndex) === '{') {
                                    curly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + 1];
                                    break;
                                }
                                else {
                                    const matches = /\s*([^\s]*)(?:\s*|$)/.exec(text.substr(firstNonWhitespaceCharacterIndex));
                                    if (matches && matches.length >= 1) {
                                        nonCurly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + matches[1].length];
                                    }
                                }
                            }
                        }
                        const match = curly || nonCurly;
                        if (match) {
                            diagnostic = new this.vscode.Diagnostic(new this.vscode.Range(match[0], match[1], match[0], match[2]), body.diagnostics[0].text);
                        }
                        else {
                            diagnostic = new this.vscode.Diagnostic(new this.vscode.Range(0, 0, 0, 0), body.diagnostics[0].text);
                        }
                        if (diagnostic) {
                            diagnostic.source = language.diagnosticSource;
                            language.configFileDiagnosticsReceived(this.client.asUrl(body.configFile), [diagnostic]);
                        }
                    }, (_error) => {
                        language.configFileDiagnosticsReceived(this.client.asUrl(body.configFile), [new this.vscode.Diagnostic(new this.vscode.Range(0, 0, 0, 0), body.diagnostics[0].text)]);
                    });
                }
            });
        }
        createMarkerDatas(diagnostics, source) {
            const result = [];
            for (let diagnostic of diagnostics) {
                const { start, end, text } = diagnostic;
                const range = new this.vscode.Range(convert_1.tsLocationToVsPosition(start, this.vscode), convert_1.tsLocationToVsPosition(end, this.vscode));
                const converted = new this.vscode.Diagnostic(range, text);
                converted.severity = this.getDiagnosticSeverity(diagnostic);
                converted.source = diagnostic.source || source;
                if (diagnostic.code) {
                    converted.code = diagnostic.code;
                }
                result.push(converted);
            }
            return result;
        }
        getDiagnosticSeverity(diagnostic) {
            if (this.reportStyleCheckAsWarnings && this.isStyleCheckDiagnostic(diagnostic.code)) {
                return this.vscode.DiagnosticSeverity.Warning;
            }
            switch (diagnostic.category) {
                case PConst.DiagnosticCategory.error:
                    return this.vscode.DiagnosticSeverity.Error;
                case PConst.DiagnosticCategory.warning:
                    return this.vscode.DiagnosticSeverity.Warning;
                default:
                    return this.vscode.DiagnosticSeverity.Error;
            }
        }
        isStyleCheckDiagnostic(code) {
            return code ? styleCheckDiagnostics.indexOf(code) !== -1 : false;
        }
    }
    exports.default = TypeScriptServiceClientHost;
});
